#include <pd_gpio.h>
#include <hd_oled.h>
#include <delay.h>
#include <hd_oled_protocol_i2c.h>
#include <hd_oled_protocol_spi.h>
#include <pd_i2c_hd.h>
#include <pd_i2c_sim.h>
#include <pd_spi_hd.h>
#include <pd_spi_sim.h>
#include <hd_flash.h>
#include <hd_w25qxx.h>

#include "main.h"

#define ARRAY_SIZE(x)       sizeof(x) / sizeof(x[0])
#define USE_HARDWARE_SPI    1
#define USE_HARDWARE_I2C    1

extern uint8_t eveSmile[];
extern uint8_t eveSerious[];

extern void SystemClock_Config(void);

void oled_static(OLED *oled);

int main(void) {
    SystemClock_Config();

    // SPI GPIO
    GPIO spi1SselGPIO[3], spi1SclkGPIO, spi1MisoGPIO, spi1MosiGPIO;
    GPIO_constructor(spi1SselGPIO + 0, GPIOA, LL_GPIO_PIN_2); // OLED
    GPIO_constructor(spi1SselGPIO + 1, GPIOA, LL_GPIO_PIN_4); // W25Q64 on chip
    GPIO_constructor(spi1SselGPIO + 2, GPIOC, LL_GPIO_PIN_4); // W25Q64 external
    GPIO_constructor(&spi1SclkGPIO, GPIOA, LL_GPIO_PIN_5);    // Clock, SPIx_SCK
    GPIO_constructor(&spi1MisoGPIO, GPIOA, LL_GPIO_PIN_6);    // MISO,  SPIx_MISO
    GPIO_constructor(&spi1MosiGPIO, GPIOA, LL_GPIO_PIN_7);    // MOSI,  SPIx_MOSI

#ifdef USE_HARDWARE_SPI
    // SPI hardware init
    SPIHardware spi1;
    SPIHardware_constructor(&spi1, SPI1, spi1SselGPIO, ARRAY_SIZE(spi1SselGPIO),
                            &spi1SclkGPIO, &spi1MosiGPIO, &spi1MisoGPIO);
#elif USE_SIMULATE_SPI
    // SPI simulate init
    SPISimulate spi1;
    SPISimulate_constructor(&spi1, spi1SselGPIO, ARRAY_SIZE(spi1SselGPIO),
                            &spi1SclkGPIO, &spi1MosiGPIO, &spi1MisoGPIO);
#endif
    SPI_init((SPI *)&spi1);

    // Storage of w25q64 init
    W25Qxx w25Qxx3OnChip;
    W25Qxx_constructor(&w25Qxx3OnChip, (SPI *)&spi1, &spi1SselGPIO[1]);
    W25Qxx_init(&w25Qxx3OnChip);

    // OLED font instance.
    OLEDFont oledFont;
    OLEDFont_contructor(&oledFont, (Storage *)&w25Qxx3OnChip);


    // OLED protocol of SPI
    GPIO oledResGPIO, oledDcGPIO;
    GPIO_constructor(&oledResGPIO, GPIOA, LL_GPIO_PIN_3); // Reset, General IO, the chip reset pin is better
    GPIO_constructor(&oledDcGPIO, GPIOA, LL_GPIO_PIN_1);  // Data or Command, General IO
    OLEDProtocolSPI oledProtocolObj;
    OLEDProtocolSPI_constructor(&oledProtocolObj, (SPI *)&spi1, &oledResGPIO, &oledDcGPIO, &spi1SselGPIO[0]);


    // OLED instance of 7-PIN(SPI)
    OLED oled1;
    OLED_constructor(&oled1, (OLEDProtocol *)&oledProtocolObj, &oledFont);

    // I2C gpio init
    GPIO oledSckGPIO, oledSdaGPIO;
    GPIO_constructor(&oledSckGPIO, GPIOB, LL_GPIO_PIN_10); // I2Cx_SCK
    GPIO_constructor(&oledSdaGPIO, GPIOB, LL_GPIO_PIN_11); // I2Cx_SDA

#ifdef USE_HARDWARE_I2C
    I2CHardware i2c2;
    I2CHardware_constructor(&i2c2, I2C2, &oledSckGPIO, &oledSdaGPIO);
#elif USE_SIMULATE_I2C
    I2CSimulate i2c2;
    I2CSimulate_constructor(&i2c2, &oledSckGPIO, &oledSdaGPIO);
#endif
    I2C_init((I2C *)&i2c2);
    // OLED protocol of I2C
    OLEDProtocolI2C oledProtocolI2c;
    OLEDProtocolI2C_constructor(&oledProtocolI2c, (I2C *)&i2c2);
    // OLED instance of 4-PIN(I2C)
    OLED oled2;
    OLED_constructor(&oled2, (OLEDProtocol *)&oledProtocolI2c, &oledFont);

    OLED_init(&oled1);
    OLED_clear(&oled1);
    OLED_refresh(&oled1);

    OLED_init(&oled2);
    OLED_clear(&oled2);
    OLED_refresh(&oled2);

    delay_ms(1000);

    oled_static(&oled1);
    oled_static(&oled2);

    while (1) {
        OLED_showImage(&oled1, 32, 0, 64, 64, eveSmile);
        OLED_refresh(&oled1);
        OLED_showImage(&oled2, 32, 0, 64, 64, eveSmile);
        OLED_refresh(&oled2);
        delay_ms(2000);

        OLED_showImage(&oled1, 32, 0, 64, 64, eveSerious);
        OLED_refresh(&oled1);
        OLED_showImage(&oled2, 32, 0, 64, 64, eveSerious);
        OLED_refresh(&oled2);
        delay_ms(2000);
    }
}

void oled_static(OLED *oled) {
    OLED_setSize(oled, OLED_SIZE_12);
    OLED_printf(oled, 0, 00, "机");
    OLED_printf(oled, 116, 00, "总");

    OLED_setSize(oled, OLED_SIZE_16);
    OLED_printf(oled, 0, 16, "器");
    OLED_printf(oled, 112, 16, "动");

    OLED_setSize(oled, OLED_SIZE_24);
    OLED_printf(oled, 0, 32, "人");
    OLED_printf(oled, 104, 32, "员");

    OLED_refresh(oled);
}
